//
//$Id: nfamin_mpi.cpp 683 2011-03-12 18:35:47Z Oleg.Bulychov $
#include "stdafx.h"
#include "contrib/simpleini/SimpleIni.h"
#include "src/heo/include/easy_mpi.h"
#include "../src/nfamin/include/utils.h"
#include "../src/nfamin/include/kw_mpi.h"
#include "../src/nfamin/include/mel_mpi.h"

using namespace std;
using namespace kw;
using namespace mel;

void MinimizeKW(vector<FA> const& vfa, KW_Parameters const& p, string const& output_dir, string const& save_format, int rand_seed, int const size, int const rank)
{
    //Subset s;

    //s.insert(130);

    for (size_t i = 0; i < vfa.size(); ++i)
    {
        //if (!s.contains(i))
        //    continue;

        if (rank == 0)
        {
            if (p.verbosity_)
            {
                PrintLine(cout, 128, '='); 

                if (vfa.size() > 1) 
                {
                    cout << endl
                         << "FA #" << i << endl;
                }
            }
            else
            {
                if (i == 0)
                {
                    cout << "FA # |RDA size|RAM rows|RAM cols|Ones dens.|Grids fnd.|Grids time, s|Min. cov.|Max. cov.|Min. NFA fnd.|Min. NFA size|Total time, s|" << endl
                         << endl;
                }

                cout << setw(5) << i << flush;
            }

            vfa[i].save(output_dir, vfa[i].name_, save_format);
        }

        KW_MPI kw(vfa[i], p, size, rank);

        if (!kw.solve(rand_seed))
        {
            return;
        }
        
        if (rank == 0)
        {
            if (p.verbosity_)
            {
                if (kw.stat_.min_a_count_)
                {
                    cout << endl 
                         << kw.stat_.min_a_count_ << " minimum state NFA(s) found" << endl;
                    
                    for (size_t j = 0; j < kw.v_min_a_.size(); ++j)
                    {
                        kw.output_FA(cout, kw.v_min_a_[j]);
                    }
                    
                    cout << endl
                         << "Total time = " << kw.stat_.total_time_ << " s" << endl;
                }
                else
                {
                    cout << endl
                         << "Minimum state NFA(s) not found!" << endl
                         << endl
                         << "Total time = " << kw.stat_.total_time_ << " s" << endl;
                }
            }
            else
            {
                cout << setw(12) << kw.stat_.min_a_count_ << setw(15) << kw.stat_.min_a_size_ 
                     << setprecision(4) << setw(16) << kw.stat_.total_time_ << endl;
            }

            for (size_t j = 0; j < kw.v_min_a_.size(); ++j)
            {
                kw.v_min_a_[j].save(output_dir, vfa[i].name_ + "_min_" + IntToStringZ(j, kw.stat_.min_a_count_ - 1), save_format);
            }
        }
    }
}

void MinimizeMel(vector<FA> const& vfa, Mel_Parameters const& p, string const& output_dir, string const& save_format, int rand_seed, int const size, int const rank)
{
    //Subset s;

    //s.insert(130);

    for (size_t i = 0; i < vfa.size(); ++i)
    {
        //if (!s.contains(i))
        //    continue;

        if (rank == 0)
        {
            if (p.verbosity_)
            {
                PrintLine(cout, 128, '='); 

                if (vfa.size() > 1) 
                {
                    cout << endl
                         << "FA #" << i << endl;
                }
            }
            else
            {
                if (i == 0)
                {
                    cout << "FA # |RDA size|RAM rows|RAM cols|Ones dens.|Grids fnd.|Grids time, s|Min. cov.|Max. cov.|Min. NFA fnd.|Min. NFA size|Total time, s|" << endl
                         << endl;
                }

                cout << setw(5) << i << flush;
            }

            vfa[i].save(output_dir, vfa[i].name_, save_format);
        }

        Mel_MPI mel(vfa[i], p, size, rank);

        if (!mel.solve(rand_seed))
        {
            return;
        }
        
        if (rank == 0)
        {
            if (p.verbosity_)
            {
                if (mel.stat_.min_a_count_)
                {
                    cout << endl 
                         << mel.stat_.min_a_count_ << " minimum state NFA(s) found" << endl;
                    
                    for (size_t j = 0; j < mel.v_min_a_.size(); ++j)
                    {
                        mel.output_FA(cout, mel.v_min_a_[j]);
                    }
                    
                    cout << endl
                         << "Total time = " << mel.stat_.total_time_ << " s" << endl;
                }
                else
                {
                    cout << endl
                         << "Minimum state NFA(s) not found!" << endl
                         << endl
                         << "Total time = " << mel.stat_.total_time_ << " s" << endl;
                }
            }
            else
            {
                cout << setw(12) << mel.stat_.min_a_count_ << setw(15) << mel.stat_.min_a_size_ 
                     << setprecision(4) << setw(16) << mel.stat_.total_time_ << endl;
            }

            for (size_t j = 0; j < mel.v_min_a_.size(); ++j)
            {
                mel.v_min_a_[j].save(output_dir, vfa[i].name_ + "_min_" + IntToStringZ(j, mel.stat_.min_a_count_ - 1), save_format);
            }
        }
    }
}

int main(int argc, char* argv[])
{
    int  rank, size;
    
    mpi::implementation impl(argc, argv);
    mpi::communicator world;
    rank = world.rank();
    size = world.size();
    //MPI_Init(&argc, &argv);
    //MPI_Comm_size(MPI_COMM_WORLD, &size);
    //MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (argc < 3)
    {
        if (rank == 0)
        {
            cout << "nfamin_mpi usage: nfamin_mpi.exe file.ini output_dir [rand_seed]" << endl;
        }
        world.abort();
        return -1;
    }

    int rand_seed;
    
    if (argc == 3)
    {
        rand_seed = 0;
    }
    else
    {
        rand_seed = atoi(argv[3]);
    }

    CSimpleIniA ini;
    bool load_ok = (ini.LoadFile(argv[1]) >= SI_OK);
    if (!load_ok) 
    {
        if (rank == 0)
        {
            cout << "nfamin_mpi: *.ini file error!" << endl;
        }
        world.abort();
        return -1;
    }

    string input_folder = ini.GetValue("nfamin", "input_folder");
    string save_format  = ini.GetValue("nfamin", "save_format");
    string method       = ini.GetValue("nfamin", "method");
 
    vector<FA> vfa;
    LoadFAs(vfa, input_folder);

    if (method == "kw")
    {
        KW_Parameters p(ini);
        MinimizeKW(vfa, p, argv[2], save_format, rand_seed, size, rank);
    }
    else
    {
        if (method == "mel")
        {
            Mel_Parameters p(ini);
            MinimizeMel(vfa, p, argv[2], save_format, rand_seed, size, rank);
        }
        else
        {
            if (rank == 0)
            {
                cout << "nfamin_mpi: unknown method!" << endl;
            }
        }
    }

    //MPI_Finalize();
    
#if defined(_MSC_VER)
    extern void If_Debug_Press_Enter();
    If_Debug_Press_Enter();
#endif
    return 0;
}

//EOF!

